{ "cells": [ { "cell_type": "markdown", "source": [ "# Iterable Objects (part II)\n", "\n", "## Try me\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ffraile/computer_science_tutorials/blob/main/source/Introduction/tutorials/Iterable%20Objects%20II.ipynb)[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ffraile/computer_science_tutorials/main?labpath=source%2FIntroduction%2Ftutorials%2FIterable%20Objects%20II.ipynb)\n", "\n", "## Tuples\n", "Tuples are very similar to lists, except that they are inmutable. This means that, once you declare a tuple variable, you cannot make any changes to it. Tuples are created using parenthesis instead of brackets, like in the example below. Since they are inmutable, we can use tuples to store values that do not change in our program. Tuples are iterable. We can access the members of a tuple using indexing and slicing and use them in control structures." ], "metadata": {} }, { "cell_type": "code", "execution_count": null, "source": [ "unknowns = (\"x\", \"y\", \"z\")\n", "# Let's print the first unknown\n", "print(\"The first unknown is: \" + unknowns[0])\n" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dictionaries\n", "\n", "A **Dictionary** is another data type to store a collection of data (like arrays).\n", "\n", "A dictionary works with **keys** and **values**. Values are accessed using keys (instead of indexes). In fact, we can think of lists as dictionaries that use integers as keys. Dictionaries can be initialized as empty dictionaries, using curved brackets (```{}```), and then using brackets to add key, value pairs as in the following example:" ], "metadata": {} }, { "cell_type": "code", "execution_count": 4, "source": [ "contacts = {}\n", "contacts[\"Paco\"] = 655555555\n", "contacts[\"Pepe\"] = 666555111\n", "contacts[\"Pili\"] = 677777555\n", "print(contacts)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "{'Paco': 655555555, 'Pepe': 666555111, 'Pili': 677777555}\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Dictionaries can also be initialized using [JSON](https://en.wikipedia.org/wiki/JSON) notation:" ], "metadata": {} }, { "cell_type": "code", "execution_count": 1, "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "print(contacts)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Paco': 655555555, 'Pepe': 666555111, 'Pili': 677777555}\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Removing elements\n", "\n", "Elements can be removed using the keyword `del` or the function `pop` of a dictionary." ], "metadata": {} }, { "cell_type": "code", "execution_count": 8, "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "del contacts[\"Pepe\"]\n", "print(contacts)\n", "\n", "contacts.pop(\"Pili\")\n", "print(contacts)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "{'Paco': 655555555, 'Pili': 677777555}\n", "{'Paco': 655555555}\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Search in a dictionary\n", "Use keyword `in` to find a key in a dictionary:" ], "metadata": {} }, { "cell_type": "code", "execution_count": 10, "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "if \"Paco\" in contacts:\n", " print(\"Paco has been found within your contacts\")" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Paco has been found within your contacts\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "The method ```values()``` returns the values of the dictionary, so we can use it to search a specific value in the dictionary." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 3, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "the number has been found in the contact list\n" ] } ], "source": [ "if 655555555 in contacts.values():\n", " print(\"the number has been found in the contact list\")" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "### For loops with dictionaries\n", "Dictionaries allow for different ways to iterate. By default, if we pass the dictionary to the ```for``` clause, the variable will take the value of each key member. You can then use the keys to access the values in the dict:" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 9, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Paco\n", "The contact name is Paco and the number 655555555\n", "Pepe\n", "The contact name is Pepe and the number 666555111\n", "Pili\n", "The contact name is Pili and the number 677777555\n" ] } ], "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "\n", "for contact in contacts:\n", " print(contact)\n", " print(\"The contact name is \", contact, \"and the number\", contacts[contact])" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "You can use the dictionary method ```values()``` to get a list of the values and iterate only over the values (if this is what you are into)." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 10, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The phone number 655555555 is in the contact list\n", "The phone number 666555111 is in the contact list\n", "The phone number 677777555 is in the contact list\n" ] } ], "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "\n", "for contact in contacts.values():\n", " print(\"The phone number\", contact, \"is in the contact list\")\n" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "We can iterate over keys and values defining two variables in the ```for``` clause and using the function ```items()``` which returns a list of tuples for every key value pair of the dictionary:" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 7, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The name of the contact is Paco and the phone number 655555555\n", "The name of the contact is Pepe and the phone number 666555111\n", "The name of the contact is Pili and the phone number 677777555\n" ] } ], "source": [ "contacts = {\n", " \"Paco\": 655555555,\n", " \"Pepe\": 666555111,\n", " \"Pili\": 677777555\n", "}\n", "for k, v in contacts.items():\n", " print(\"The name of the contact is\", k, \"and the phone number\", v)" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "## Nested iterables\n", "We can nest iterables, one inside the other, for instance, to build a list of lists, or a list of dictionaries:\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 13, "outputs": [], "source": [ "# my_nested_list_1 is a list of lists\n", "my_nested_list_1 = [[1, 2],\n", " [3, 4]]\n", "# Note that it is very similar to a matrix!\n", "\n", "# my_nested_list_2 is a list of dictionary that contains data from different students. Each dictionary contains the data of a student\n", "my_nested_list_2 = [{\"name\": \"Pepe\", \"age\":19},\n", " {\"name\": \"Ingrid\", \"age\": 18}]\n", "\n", "# my_nested_list_3 is similar to my_list_2, but each dictionary contains a list of favourite colors.\n", "my_nested_list_3 = [{\"name\": \"Pepe\", \"age\":19, \"favourite_colors\": [\"Orange\", \"Blue\"]},\n", " {\"name\": \"Ingrid\", \"age\": 18, \"favourite_colors\": [\"Orange\", \"Blue\"]}]\n" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "Indexing will work, just the same. Taking the first example, the first member of the list can be accessed with ```my_nest_list_1[0]``` and is also a list, so, we can access the first member using ```my_nest_list_1[0][0]````:\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 14, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n", "1\n" ] } ], "source": [ "print(my_nested_list_1[0]) #This returns the first (row) list\n", "print(my_nested_list_1[0][0]) #And from the first list, we can list the first number" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "Similarly, we can access the value of the 'name' key in the second member of ```my_nest_list_2``` using a similar procedure:" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 15, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ingrid\n" ] } ], "source": [ "print(my_nested_list_2[1][\"name\"])" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "Can you use the skills that you build to collect the names and grades of a group of students and calculate their average grade? Try to complete the following template to achieve this:" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 16, "outputs": [], "source": [ "students = [] #This list will hold the data\n", "keys = (\"name\", \"grade\") # This tuple is used to create the dictionaries with the data of each student\n", "while True:\n", " response = input(\"Do you want to enter a new student in the list? (Y/N)\")\n", " if response == \"Y\":\n", " student = {}\n", " for key in keys:\n", " value = input(\"Enter the students' \" + key)\n", " student[key] = value\n", " students.append(student)\n", " elif response == \"N\":\n", " break\n", " else:\n", " print(\"I did not understand your response, please enter Y for yes and N for No\")\n" ], "metadata": { "collapsed": false } }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Comprehension\n", "Comprehension is a nice feature of Python that allows to create iterables in an efficient way. Comprehension uses a for loop in the initialisation of the iterable:" ] }, { "metadata": {}, "cell_type": "code", "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[1, 4, 9, 16]\n", "{'a': 1, 'b': 4, 'c': 9, 'd': 16}\n" ] } ], "execution_count": 14, "source": [ "keys = ('a', 'b', 'c', 'd')\n", "values = (1, 2, 3, 4)\n", "\n", "# Array comprehension\n", "squares = [i**2 for i in values]\n", "print(squares)\n", "\n", "## Array comprehension with condition\n", "even_squares = [i**2 for i in values if i%2 == 0]\n", "print(even_squares)\n", "\n", "# Dictionary comprehension\n", "new_dict = {keys[i]:squares[i] for i in range(len(keys))}\n", "print(new_dict)\n" ] }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Packing and Unpacking\n", "Tuples and dictionaries are used in Python to pack an arbitrary number of members, with or without keys. Unpacking a tuple or array means accessing their members. To unpack an arbitrary number of members of a tuple we use the * operator" ] }, { "metadata": {}, "cell_type": "code", "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "1\n", "2\n", "3\n", "4\n", "1\n", "[2, 3]\n", "4\n", "1\n", "3\n", "5\n", "7\n", "9\n" ] } ], "execution_count": 3, "source": [ "t = (1, 2, 3, 4)\n", "a, b, c, d = t #This unpacks each member of the tuple in an integer variable\n", "print(a)\n", "print(b)\n", "print(c)\n", "print(d)\n", "\n", "first, *g, last = t #This unpacks the first member of t in an integer f, an arbitrary number of members in list g and the last member in last\n", "print(first)\n", "print(g)\n", "print(last)\n" ] }, { "metadata": {}, "cell_type": "markdown", "source": "Unpacking is normally used to pass an arbitrary number of parameters to a function, for instance, let us look again to the range function" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "range_args = (1,10,2)\n", "for i in range(*range_args): #Unpack the members and pass them as params to the range function\n", " print(i)" ] }, { "metadata": {}, "cell_type": "markdown", "source": "To unpack a dictionary, the operator is `**` instead of `*`. Let us revisit the print function to print two strings instead of one, and overwrite the separator:" }, { "metadata": {}, "cell_type": "code", "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "first string, second_string\n", "first string, second string\n" ] } ], "execution_count": 7, "source": [ "print(\"first string\", \"second_string\", sep=', ', end='\\n') # Print two strings separated by ', ' and terminated with a new line:\n", "args = (\"first string\", \"second string\")\n", "key_args = {\"sep\":', ', \"end\": '\\n'}\n", "print(*args, **key_args) # This is the same but unpacking a tuple with the parameters and a dictionary with the keyword params sep and end" ] }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Zip function\n", "zip is a handy function that returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables:" ] }, { "metadata": {}, "cell_type": "code", "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "1, 4\n", "2, 5\n", "3, 6\n" ] } ], "execution_count": 9, "source": [ "x = [1, 2, 3]\n", "y = [4, 5, 6]\n", "zipped = zip(x, y)\n", "for a, b in zipped:\n", " print(a, b, sep=', ')\n" ] } ], "metadata": { "kernelspec": { "name": "python3", "display_name": "Python 3.8.1 64-bit" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.1" }, "interpreter": { "hash": "2db524e06e9f5f4ffedc911c917cb75e12dbc923643829bf417064a77eb14d37" } }, "nbformat": 4, "nbformat_minor": 4 }